가장 먼저 텐서플로우 패키지를 임포트해야 합니다. 보통 임포트하고 나서 이름을 축약해서 사용하려고 as 키워드를 사용합니다.
import tensorflow as tf
In [ ]:
텐서플로우는 계산 그래프를 만들고 실행하는 두가지를 구분하고 있습니다. 두개의 상수를 더하는 그래프를 만들고 실행시켜 보겠습니다.
텐서플로우에서 상수를 만드는 함수는 tf.constant() 입니다. 텐서플로우에서는 이런 함수들을 API라고 부릅니다. 여기서는 편의상 함수라고 부르겠습니다. 이 함수를 사용해서 두개의 상수를 만들어 보세요.
a = tf.constant(2)
In [ ]:
위에서 구한 두개의 상수를 덧셈하겠습니다. 텐서플로우의 덧셈 함수는 tf.add() 입니다.
c = tf.add(a, b)
In [ ]:
지금까지 우리는 그래프를 만들었습니다. 이 그래프는 실제로 실행되지 않았고 그래프의 구조만 정의한 것입니다. 그래프를 실행하려면 tf.Session() 객체를 만들어 계산하려는 그래프를 전달하면 됩니다.
sess = tf.Session()
sess.run(c)
계산 그래프의 다른 부분을 넣으면 그 노드에서 필요한 하위 노드를 계산하여 결과를 돌려 줍니다. 위에서 만든 상수와 덧셈 노드를 모두 run() 함수로 실행해 보세요.
In [ ]:
텐서플로우에서는 텐서(Tensor)라 부르는 다차원 배열을 데이터의 기본 구조로 사용합니다. 기술적으로 말하면 계산 그래프의 노드 사이를 이동하는 데이터가 텐서입니다.
텐서의 크기는 shape 속성을 참조해서 확인할 수 있습니다. 위에서 만든 상수 텐서와 결과의 크기를 확인해 보세요.
a.shape
In [ ]:
배열이 아닌 값 하나로 이루어진 것을 스칼라(scalar)라고 부릅니다. 따러서 스칼라 텐서의 크기는 없습니다.
이번에는 파이썬의 리스트를 이용하여 텐서를 만들어 보겠습니다. 임의의 정수 리스트를 tf.constant()에 입력하고 크기를 확인해 보세요.
c = tf.constant([1, 1])
c.shape
In [ ]:
텐서플로우에서는 텐서의 크기를 차원(dimension)이라고 부릅니다. 리스트의 리스트를 만들면 차원이 두개인 텐서를 만들 수 있습니다.
d = tf.constant([[0, 1, 2], [3, 4, 5]])
이런 텐서는 이차원 구조의 텐서가 됩니다. 이차원 텐서를 만들고 텐서의 크기와 값을 출력해 보세요. sess.run() 명령을 사용해야 값을 확인할 수 있습니다.
In [ ]:
자주 사용되는 텐서를 편리하게 만들 수 있는 함수들이 있습니다.
tf.zeros()는 지정된 크기의 텐서를 만든 후 모두 0으로 채워서 리턴합니다. tf.ones()는 지정된 크기의 텐서를 만든 후 모두 1로 채워서 리턴합니다.
tf.zeros()와 tf.ones()로 임의의 크기의 텐서를 만든 후 값을 출력해 보세요.
In [ ]:
정규분포 또는 가우시안 분포는 종 모양의 확률 분포를 말합니다. 랜덤한 난수를 발생시킬 때 정규 분포를 따르는 난수가 필요한 경우가 많습니다. 이 때 사용하는 함수는 tf.random_normal() 입니다. 이 함수의 기본값은 평균은 0, 표준편차는 1인 정규분포의 난수를 발생시킵니다.
tf.random_normal()로 임의의 크기의 텐서를 만든 후 값을 출력해 보세요.
In [ ]:
파이썬의 대표적인 그래프 라이브러리는 matplotlib 입니다. 주피터 노트북에서는 매직 커맨드라고 부르는 % 기호로 시작하는 명령들이 있습니다. 아래 명령은 matplotlib 패키지를 임포트하고 matplotlib 으로 그린 그래프를 주피터 노트북에 함께 포함되도록 해줍니다.
아래 셀은 그냥 실행하세요.
In [ ]:
import matplotlib.pyplot as plt
%matplotlib inline
위에서 사용한 tf.random_normal() 함수를 사용해 100개의 난수를 만들어 보세요.
rnd = tf.random_normal([100])
data = sess.run(rnd)
In [ ]:
plt 약자를 이용해서 matplotlib의 그래프 함수를 호출할 수 있습니다. plt.hist() 함수를 사용하면 히스토그램을 그릴 수 있습니다. 위에서 만든 100개의 난수 데이터 data 의 히스토그램을 그려 보세요. 정규 분포를 따르고 있나요?
In [ ]:
tf.truncated_normal() 함수는 정규분포의 난수를 발생시키지만 표준편차 2범위 안의 값만을 구합니다. 이는 너무 큰 값의 난수를 생성하지 않게 하려고 함입니다.
tf.truncated_normal()로 위와 같이 100개의 텐서를 만든 후 히스토그램을 그려 보세요.
In [ ]:
tf.random_uniform() 함수는 균등분포의 난수를 발생시킵니다. 위에서와 같이 100개의 텐서를 만든 후 히스토그램을 그려 보세요.
In [ ]:
텐서의 계산은 차원을 고려하여 수행됩니다. 두개의 원소를 갖는 텐서 두개를 만들어 덧셈을 해 보세요. 결과는 sess.run() 함수를 사용해야 구할 수 있습니다. 텐서의 원소별로 덧셈이 이루어졌나요?
a = tf.constant([10, 20])
In [ ]:
이번에는 2차원 텐서 두개를 만들어 뺄셈을 해 보겠습니다. 뺄셈을 하는 명령은 tf.subtract() 입니다.
a = tf.constant([[10, 20], [30, 40]])
b = tf.constant([[10, 20], [30, 40]])
c = tf.subtract(a, b)
In [ ]:
텐서의 사칙연산은 tf.add(), tf.subtract() 대신에 +, - 연산자를 사용할 수 있습니다. 하지만 +, - 연산자를 사용하더라도 실제 계산이 되는 것이 아니고 그래프를 만드는 과정입니다. 실제 계산은 run() 명령을 실행해야 합니다.
위에서 계산한 덧셈, 뺄셈을 +, - 연산자를 사용해 계산해 보세요.
In [ ]:
텐서의 곱셈, 나눗셈 연산 함수는 tf.multiply(), tf.divide() 입니다. 함수 대신에 *, / 연산자를 사용하여 위에서 만든 두 텐서를 곱하고 나누어 보세요.
In [ ]:
상수는 그래프의 일부분으로 수정될 수 없는 값입니다. 머신 러닝에서 알고리즘이 데이터로부터 학습한 결과를 저장할 도구가 필요합니다. 이럴 때 사용하는 것이 변수, tf.Variable() 입니다.
tf.Variable()로 변수를 만들 때에 초깃값이 주어져야 합니다. 상수 텐서를 이용하여 변수를 만들어 보세요.
a = tf.Variable(...)
In [ ]:
변수를 계산 그래프에 추가하기 위해서는 초기화 과정을 하나더 거쳐야 합니다. tf.global_variables_initializer() 는 필요한 모든 변수를 그래프에 추가하여 계산 과정에 참여할 수 있도록 합니다.
init = tf.global_variables_initializer()
sess.run(init)
변수를 초기화한 후에 위에서 만든 변수값을 run() 명령을 사용해 출력해 보세요.
In [ ]:
변수의 용도를 알기 위해 변수의 값을 바꾸는 계산 그래프를 만들어 보겠습니다.
a = tf.Variable(tf.constant(2))
upd = a.assign_add(tf.constant(3))
init = tf.global_variables_initializer()
sess.run(init)
sess.run(upd)
In [ ]:
sess.run(upd) 명령을 여러번 실행해 보세요 값이 어떻게 바뀌나요?
In [ ]:
변수를 초기화할 때 수동으로 값을 지정하지 않고 난수를 발생시켜 채우는 경우가 많습니다. tf.random.normal() 함수를 사용하여 임의의 크기의 변수를 만들고, 변수를 초기화한 다음에, run() 명령으로 값을 출력해 보세요.
In [ ]:
2차원 텐서의 경우 원소를 참조하려면 인덱스를 두개 사용합니다.
a[1][2]
첫번째 인덱스는 행을 나타내고 두번째 인덱스는 열을 지칭합니다.
a = tf.Variable([[1, -2, 2], [3, -1, 1]])
sess.run(tf.global_variables_initializer())
sess.run(a[1][2])
2x3 크기의 텐서를 만들고 행, 열을 지정하여 값을 출력해 보세요.
In [ ]:
tf.reduce_sum()은 행, 열 방향으로 또는 전체의 합을 계산합니다.
tf.reduce_sum(a, 0)
첫번째 매개변수는 대상 텐서를 지정하고 두번째 매개변수는 행(0) 또는 열(1) 방향을 지정합니다. 두번째 매개변수에 아무런 값도 지정하지 않으면 행렬 전체에 대한 합을 계산합니다.
위에서 만든 텐서의 행, 열 방향 합을 구해 보세요. 그리고 행렬의 전체 합도 계산해 보세요.
In [ ]:
tf.reduce_mean()은 tf.reduce_sum()과 아주 비슷하게 동작합니다. 다른 점은 합을 계산하는 것이 아니라 평균을 계산합니다.
위에서 만든 텐서의 행, 열 방향 평균을 구해 보세요. 그리고 행렬의 전체 평균도 계산해 보세요.
In [ ]:
뉴럴 네트워크 알고리즘은 행렬 계산으로 이루어져 있다고 해도 과언이 아닙니다. 그 중에서도 행렬의 내적이 많이 사용됩니다. 내적은 두 행렬을 곱할 때 첫번째 행렬의 행과 두번째 행렬의 열을 곱하여 결과를 만듭니다.
따라서 내적을 하려면 첫번째 행렬의 행과 두번째 행렬의 열의 차원이 같아야 합니다. 예제를 위해 tf.Variable() 함수를 사용하여 2x3 크기의 변수와 3x2 크기의 변수를 만들어 보세요.
In [ ]:
내적을 하는 명령은 tf.matmul() 입니다. 위에서 만든 두개의 텐서를 matmul() 함수를 이용해 곱해 보세요. 곱한 결과를 보려면 변수를 초기화하고 run() 명령을 사용해야 합니다.
dot = tf.matmul(a, b)
In [ ]:
앞에서 언급한 대로 변수는 알고리즘이 데이터로부터 학습한 것을 저장하는 용도로 사용됩니다. 알고리즘에 데이터를 전달하기 위해 상수를 사용하려면 매번 그래프를 새로 만들어 주어야 합니다. 뉴럴 네트워크 알고리즘은 반복이 많이 필요로 하기 때문에 매번 그래프를 만들어 주면 저장한 변수 값을 잃게 됩니다.
엔지니어가 학습 데이터를 반복 과정 중간에 주입할 도구가 필요한데 이를 플레이스홀더(placeholder)라고 합니다.
위에서 만든 3x2 크기의 변수를 대신하는 동일 크기의 플레이스홀더를 만들겠습니다. 플레이스홀더는 tf.placeholder() 함수로 만듭니다. 플레이스 홀더를 만들 때 입력할 데이터 타입을 지정해 주어야 합니다. tf.int32, tf.float32 등이 있습니다.
b = tf.placeholder(tf.int32, [3, 2])
이 플레이스홀더는 값이 없는 채로 계산 그래프에 추가됩니다.
In [ ]:
tf.matmul() 함수를 사용하여 2x3 크기의 변수와 3x2 크기의 플레이스홀더를 내적합니다. 그런다음 변수를 초기화를 위한 global_variables_initializer() 함수를 호출합니다.
dot = tf.matmul(a, b)
init = tf.global_variables_initializer()
sess.run(init)
In [ ]:
마지막으로 sess.run(dot, ...) 함수를 호출할 때 두번째 인자 feed_dict 로 플레이스홀더 b 에 3x2 크기의 배열을 전달합니다.
sess.run(dot, feed_dict={b: [...]})
feed_dict 매개변수의 값은 딕셔너리이며 키는 플레이스홀더이고 값은 플레이스홀더에 지정한 크기와 같아야 합니다. 보통 이렇게 feed_dict 에 값을 전달하는 과정을 데이터를 주입한다라고 종종 표현합니다.
In [ ]:
동일한 b 플레이스홀더에 위와 다른 값을 주입해 계산해 보세요. 계산 그래프를 다시 만들지 않아도 b 값에 따라서 결과가 변경됩니다.
In [ ]:
수고하셨습니다! ^^